#include "allocator.h"

void chunk::init( uint32 blocksize, unsigned char blockcount )
{
    pData_ = new unsigned char[blocksize * blockcount];
    firstFree_ = 0;
    blockCount_ = blockcount;
    unsigned char i = 0;
    unsigned char* p = pData_;
    for( ; i<blockcount; p += blocksize )
        *p = ++i;
}

void chunk::release()
{
    if(pData_)
    {
        delete []pData_;
        pData_ = NULL;
    }
}

void * chunk::alloc( uint32 blocksize )
{
    if( blockCount_ == 0 )
        return NULL;
    unsigned char *pResult = pData_+(blocksize * firstFree_);
    firstFree_ = *pResult;
    blockCount_--;
    return pResult;
}

void chunk::dealloc( void* p, uint32 blocksize )
{
    assert( p >= pData_ );
    unsigned char* toRelease = static_cast<unsigned char*>(p);
    assert( (toRelease-pData_)%blocksize == 0 );
    *toRelease = firstFree_;
    firstFree_ = (toRelease-pData_)/blocksize;
    blockCount_++;
}

FixedAllocator::FixedAllocator( uint32 blocksize , unsigned char blockcount ):blocksize_(blocksize),blockcount_(blockcount)
{
    allocChunk_ = NULL;
    deallocChunk_ = NULL;
    chunks_.reserve(16);
}

FixedAllocator::FixedAllocator()
{
    allocChunk_ = NULL;
    deallocChunk_ = NULL;
    chunks_.reserve(16);
}
FixedAllocator::~FixedAllocator()
{
    Release();
}

void FixedAllocator::Initialize( uint32 blocksize, unsigned char blockcount )
{
    blocksize_ = blocksize;
    blockcount_ = blockcount;
}

void * FixedAllocator::Allocate()
{
    if( allocChunk_ == NULL || allocChunk_->blockCount_ == 0 )
    {
        Chunks::iterator iter = chunks_.begin();
        for( ; ; ++iter )
        {
            if( iter == chunks_.end() )
            {
                chunks_.push_back( chunk() );
                allocChunk_ = &chunks_.back();
                allocChunk_->init( blocksize_, blockcount_ );
                deallocChunk_ = &chunks_.front();
                break;
            }
            if( iter->blockCount_ > 0 )
            {
                allocChunk_ = &(*iter);
                break;
            }
        }
    }
    assert( allocChunk_ != NULL );
    assert( allocChunk_->blockCount_ > 0 );
    return allocChunk_->alloc( blocksize_ );
}

void FixedAllocator::Dealloc( void* p )
{
    unsigned char* toRelease = static_cast<unsigned char*>(p);
    if( deallocChunk_ == NULL || toRelease < deallocChunk_->pData_ || toRelease > deallocChunk_->pData_+blockcount_*blocksize_ )
    {
        deallocChunk_ = NULL;
        Chunks::iterator iter = chunks_.begin();
        for( ; iter != chunks_.end(); ++iter )
        {
            deallocChunk_ = &*iter;
            if( toRelease >= deallocChunk_->pData_ && toRelease < deallocChunk_->pData_+blockcount_*blocksize_ )
            {
                break;
            }
        }
    }
    assert( deallocChunk_ != NULL );
    deallocChunk_->dealloc(p, blocksize_);
}

void FixedAllocator::Release()
{
    Chunks::iterator iter = chunks_.begin();
    for( ; iter != chunks_.end(); ++iter )
    {
        (*iter).release();
    }
}

bool SmallObjAllocator::m_bDestroyed = false;
SmallObjAllocator *SmallObjAllocator::m_pInstance = NULL;

SmallObjAllocator::SmallObjAllocator(  )
{
    chunksize_ = DefaultChunkSize;
    maxobjsize_ = DefaultSmallObjSize;
    for(int i=0; i<DefaultSmallObjSize; i++)
        pool_[i].Initialize( i, DefaultBlockCount );
}

SmallObjAllocator::~SmallObjAllocator(  )
{
    m_bDestroyed = true;
}

void * SmallObjAllocator::Allocate( uint32 numBytes )
{
    Guard test(m_lock);
    if( numBytes >= maxobjsize_ )
        return operator new(numBytes);
    return pool_[numBytes].Allocate();
}

void SmallObjAllocator::Dealloc( void* p, uint32 size )
{
    Guard test(m_lock);
    if( size >= maxobjsize_ )
        operator delete(p);
    return pool_[size].Dealloc(p);
}

void * SmallObject::operator new( std::size_t size )
{
    return SmallObjAllocator::Instance().Allocate( size );
}

void SmallObject::operator delete( void *p, std::size_t size )
{
    SmallObjAllocator::Instance().Dealloc( p, size );
}